/* eslint-disable max-lines */
import { conforms, debounce } from "lodash";
import { generateFormItem } from "./generator";
import { setFormDefaultDataByConfigList } from "@utils/form.js";
import { formatDate } from "@utils";

const noop = function () { };

export default {
    name: "com-commonForm",
    props: {
        configs: { type: Array | Function, default: () => [] },
        inline: { type: Boolean, default: false },
        disabled: { type: Boolean, default: false },
        labelWidth: { type: String | Number, default: "100px" },
        labelPosition: { type: String, default: "right" },
        initFormData: { type: Object },
        labelSuffix: { type: String, default: ":" },
        col: { type: Number, default: 4 },
        size: { type: String, default: 'small' },
        wrappers: { type: Object },
        formId: {type: String, default: 'el-form'},
        submitPrevent: {
            type: Boolean,
            default: false
        }
    },
    data() {
        let formData = {},
            defaulteData = {
                updateFormSign: false,
                formDataSettingSign: false,
                notToTriggerConfigFunctionSign: false,
                itemDependonWatchMap: {},
                itemChangeToEmitWatchMap: {},
                notToEmitChangeOnceFiledsMap: {},
                functionBackFormData: null
            };

        if (Array.isArray(this.configs)) {
            let initData = this.getInitFormData(this.configs);
            formData = Object.assign(initData, this.initFormData || {});

            this.$nextTick(() => {
                this.loadAsyncData();
            });

            return {
                ...defaulteData,
                formData,
                configData: [...this.configs]
            };
        }

        defaulteData.formDataSettingSign = true;

        this.configs()
            .then(result => {
                this.functionBackFormData = { ...result.formData };
                this.formData = result.formData;
                this.configData = result.configData;
                this.formDataSettingSign = false;
                this.$emit("setFormDataEnd");

                this.debounceUpdateFunction = debounce(
                    this.updateFunctionalConfig,
                    300
                );

                this.$nextTick(() => {
                    this.loadAsyncData();
                    this.updateUnWatchHandle = this.$watch(
                        "formData",
                        this.debounceUpdateFunction,
                        {
                            deep: true
                        }
                    );
                });
            })
            .catch(err => {
                console.log("form config init function err", err);
                this.formDataSettingSign = false;
                this.$emit("setFormDataEnd");
            });

        return {
            ...defaulteData,
            formData,
            configData: []
        };
    },
    watch: {
        configs(val, oldVal) {
            // if (val === oldVal) {
            //     return;
            // }
            if (val && typeof val !== "function") {
                this.configData = [...val];
                this.formData = {
                    ...setFormDefaultDataByConfigList(
                        this.configs,
                        this.formData
                    )
                }
                this.unWatchMap();
                this.$nextTick(() => {
                    this.loadAsyncData();
                });
            }
        },
       
    },
    render(h) {
        let formItemList = [],
            rules = {},
            colDivSign = this.col >= 1 && this.inline === false;

        this.configData.forEach(item => {
            let formItem = null;

            if (item.required) {
                let message = item.placeholder;

                if (item.label) {
                    message = `请${item.type === "select" || item.type === "multiselect" ? "选择" : "输入"}${item.label
                        }`;
                }

                let trigger = "blur";

                if (item.type === "cascader") {
                    trigger = ["blur", "change"];
                }
                const changeArr = ['editor', 'select', 'render']
                if (changeArr.includes(item.type)) {
                    trigger = "change";
                }
                let requiredRule = {
                    required: true,
                    message,
                    trigger
                };

                if (rules[item.key]) {
                    rules[item.key].push(requiredRule);
                } else {
                    rules[item.key] = [requiredRule];
                }
            }

            if (item.rules) {
                rules[item.key] = item.rules.concat(rules[item.key] || []);
            }

            if (item.getParent && item.type === "cascader") {
                item.ref = item.key + 'Ref'
            }

            if (item.type === "empty") {
                formItem = <span>&nbsp;</span>;
            } else if (item.type === "string") {
                formItem = <span class="showMoreRows-1 overflow-hidden" title={this.formData[item.key]}>{this.formData[item.key]}</span>;
            } else if (item.type === "render" && item.render) {
                formItem = item.render(
                    h,
                    this,
                    item,
                    this.formData[item.key],
                    data => {
                        this.formData[item.key] = data;
                    }
                );
            } else {
                formItem = generateFormItem(h, this, item);
            }
            
             let className=''
             if(item.className){
                className=item.className;
                
             }
            formItem = (
                <el-form-item
                    key={item.key}
                    label={item.label}
                    prop={item.key}
                    label-width={item.labelWidth ? item.labelWidth : ''}
                    class={"el-item-w-100 " + className}
                    {...{
                        props: {
                            ...(item.noLabel ? { "label-width": "0px" } : {}),
                            ...item.itemProps
                        }
                    }}
                >
                    {
                        (item.noLabel || item.type === 'empty') ? null : 
                        item.labelRender ? 
                            <com-functionRender
                                slot="label"
                                render={item.labelRender}
                                data={{
                                    vm: this,
                                    formData: this.formData,
                                    configItem: item,
                                }}
                            ></com-functionRender>
                         :
                         <span  slot="label" title={item.label || ''}>{(item.label||"") + (item.labelSuffix || this.labelSuffix)}</span>
                    }
                    {formItem}
                </el-form-item>
            );
            let mclear='',mborder='',padding=''
            if(item.mclear){
                mclear=' '+item.mclear
            }
            if(item.mborder){
                mborder=' '+item.mborder
            }
            if(item.padding){
                padding=' '+item.padding
            }
            if (colDivSign) {
                formItem = (
                    <el-col
                        span={
                            item.fullLine
                                ? 24
                                : Math.floor(24 / this.col) * (item.col || 1)
                        }
                        class={"form_item_col px-16"+mclear+mborder+padding} 
                    >
                        {formItem}
                    </el-col>
                );
            }

            formItemList.push(formItem);
        });

        formItemList = this.handleWrapperFormItemList(h, formItemList);
        if (colDivSign) {
            formItemList = <el-row type="flex" style="flex-wrap: wrap;">{formItemList}</el-row>;
        }

        return (
            <el-form
                id={this.formId}
                {...{
                    class: "",
                    on: {
                        input: noop
                    },
                    nativeOn: {
                        ...(
                            this.submitPrevent ? {
                                submit: (e) => {
                                    e.preventDefault && e.preventDefault();
                                    e.stopPropagation && e.stopPropagation();
                                }
                            }
                            : {}
                        )
                    },
                    props: {
                        model: this.formData,
                        rules: rules,
                        inline: this.inline,
                        disabled: this.disabled,
                        size: this.size,
                        "label-position": this.labelPosition,
                        "label-width": this.labelWidth,
                        "label-suffix": this.labelSuffix,
                        "validate-on-rule-change": false
                    },
                    ref: "form",
                    key: "form"
                }}
            >
                {formItemList}
                {this.$slots.inline}
            </el-form>
        );
    },
    methods: {
        // 重组formItem, 如果有wrapper, 将formItem插入wrapper
        handleWrapperFormItemList(h, formItemList) {
            let { wrappers, configData } = this;
            let outPutFormItemList = [];
            if (!wrappers) return formItemList;
            let temSlotMap = {
                slotIn: '',
                list: []
            };

            const renderWrapper = (wrapper, itemForm) => {
                if (!wrapper) {

                    return itemForm;

                } else if (wrapper && typeof wrapper === "function") {

                    return wrapper(h, { dom: itemForm, vm: this });
                } else {
                    return <wrapper>
                        {itemForm}
                    </wrapper>
                }
            }

            formItemList.forEach((item, index) => {
                let configItem = configData[index];
                let slotIn = configItem.slotIn;
                let wrapper = wrappers[temSlotMap.slotIn];

                let hasSlotIn = slotIn && wrappers[slotIn];
                if (!hasSlotIn) {
                    if (temSlotMap.slotIn) {
                        outPutFormItemList.push(renderWrapper(wrapper, temSlotMap.list));
                        temSlotMap = {
                            slotIn: '',
                            list: []
                        }
                    }
                    outPutFormItemList.push(item);
                } else {
                    if (temSlotMap.slotIn && temSlotMap.slotIn !== slotIn) {
                        outPutFormItemList.push(renderWrapper(wrapper, temSlotMap.list));
                        temSlotMap = {
                            slotIn: '',
                            list: []
                        }
                    }

                    temSlotMap.slotIn = slotIn;
                    temSlotMap.list.push(item);
                    if (index === formItemList.length - 1) {
                        outPutFormItemList.push(renderWrapper(wrapper, temSlotMap.list));
                    }
                }
            })

            return outPutFormItemList;
        },
        getInitFormData(configs) {
            configs = configs || this.configData;

            let formData = {};

            configs.forEach(config => {
                let defaultData = null;

                if (typeof config.default === "function") {
                    defaultData = config.default();
                } else if (typeof config.default !== "undefined") {
                    defaultData = config.default;
                }

                formData[config.key] = defaultData;
            });

            return formData;
        },
        async loadItemAsyncData(config, configIndex) {
            if (
                (config.type === "select" ||
                    config.type === "checkbox" ||
                    config.type === "allCheckbox" ||
                    config.type === "multiselect" ||
                    config.type === "cascader" ||
                    config.type === "radio" ||
                    config.type === "stringOptions") &&
                (typeof config.options === "function" ||
                    typeof config.backOptionFunction === "function")
            ) {
                let options = [];

                if (typeof config.options === "function") {
                    config.backOptionFunction = config.options;

                    options = await config.options(this.formData);
                } else {
                    options = await config.backOptionFunction(this.formData);
                }

                this.configData[configIndex].options = options;
            }
        },
        getOptions(key) {
            return _.get(this.configData.find((item, index) => index === this.configData.findIndex(i => i.key === key)), "options");
        },
        updateConfigs(val) {
            this.configData = [...val];
            this.formData = {
                ...setFormDefaultDataByConfigList(
                    val,
                    this.formData
                )
            }
            this.unWatchMap();
            this.$nextTick(() => {
                this.loadAsyncData();
            });
        },
        updateOptions(key, options) {
            this.configData.forEach((item, index) => {
                if(item.key === key) {
                    this.configData[index].options = options;
                }
            })
        },
        updateConfigsAttr(key, attr,value) {
            this.configs.forEach((item, index) => {
                if(item.key === key) {
                    this.configs[index][attr] = value;
                }
            })
            this.$forceUpdate()
        },
        async loadAsyncData() {
            let configs = this.configData,
                processList = configs.map((config, configIndex) => {
                    if (config.dependon) {
                        const bindWatch = () => {
                            this.itemDependonWatchMap[
                                config.key
                            ] = config.dependon.map(v => {
                                return this.$watch(
                                    `formData.${v}`,
                                    itemWatchHandle
                                );
                            });
                        };

                        const itemWatchHandle = async () => {
                            this.formData[config.key] = null;

                            await this.loadItemAsyncData(config, configIndex);

                            let watchList =
                                this.itemDependonWatchMap[config.key] || [];

                            watchList.forEach(f => {
                                f();
                            });

                            bindWatch();
                        };

                        bindWatch();
                    }

                    if (config.changeToEmit) {
                        this.itemChangeToEmitWatchMap[config.key] = this.$watch(
                            `formData.${config.key}`,
                            (val, oldVal) => {
                                if (
                                    this.notToEmitChangeOnceFiledsMap[
                                    config.key
                                    ]
                                ) {
                                    this.notToEmitChangeOnceFiledsMap[
                                        config.key
                                    ] = false;
                                    return;
                                }
                                this.$emit("changeOut", {
                                    key: config.key,
                                    config,
                                    data: this.formData[config.key],
                                    formData: this.formData,
                                    oldValue: oldVal,
                                    vm: this
                                });
                            }
                        );
                    }

                    if (config.getParent) {
                        this.itemChangeToEmitWatchMap[config.key] = this.$watch(
                            `formData.${config.key}`,
                            (val, oldVal) => {
                                setTimeout(() => {
                                    const checkedValues = this.$refs[config.ref].getCheckedNodes().map(item => {
                                        return item.value
                                    })
                                    this.formData[config.getParent] = checkedValues
                               },400)
                            }
                        );
                    }
                    return this.loadItemAsyncData(config, configIndex);
                });

            await Promise.all(processList);

            this.$forceUpdate();
        },
        updateFunctionalConfig(n, o) {
            if (typeof this.configs !== "function") {
                return;
            }

            if (this.notToTriggerConfigFunctionSign) {
                this.notToTriggerConfigFunctionSign = false;
                return;
            }

            if (this.updateUnWatchHandle) {
                this.updateUnWatchHandle();
                this.updateUnWatchHandle = null;
            }

            // if (this.updateFormSign) {
            //     this.updateFormSign = false;
            //     return;
            // }

            this.formDataSettingSign = true;

            this.configs({
                formData: this.formData,
                configData: this.configData,
                oldFormData: this.functionBackFormData,
                vm: this
            })
                .then(result => {
                    this.functionBackFormData = { ...result.formData };

                    if (!result.notChange) {
                        this.formData = result.formData;
                        this.configData = result.configData;

                        this.updateFormSign = true;
                    }

                    this.$nextTick(() => {
                        this.updateUnWatchHandle = this.$watch(
                            "formData",
                            this.debounceUpdateFunction,
                            {
                                deep: true
                            }
                        );

                        this.$nextTick(() => {
                            console.log("watching out formData", this.formData);
                        });

                        this.formDataSettingSign = false;
                        this.$emit("setFormDataEnd");
                    });
                })
                .catch(err => {
                    console.log("form config function err", err);

                    this.updateUnWatchHandle = this.$watch(
                        "formData",
                        this.debounceUpdateFunction,
                        {
                            deep: true
                        }
                    );

                    this.formDataSettingSign = false;
                    this.$emit("setFormDataEnd");
                });
        },
        setFormData(
            formData,
            { notToEmitChangeFileds, notToTriggerConfigFunction, delay } = {}
        ) {
            const setNotToEmit = () => {
                if (notToEmitChangeFileds && notToEmitChangeFileds.length) {
                    let map = {};
                    notToEmitChangeFileds.forEach(v => {
                        map[v] = true;
                    });
                    this.notToEmitChangeOnceFiledsMap = {
                        ...this.notToEmitChangeOnceFiledsMap,
                        ...map
                    };
                }

                if (notToTriggerConfigFunction) {
                    this.notToTriggerConfigFunctionSign = true;
                }
            };

            if (this.formDataSettingSign) {
                this.$once("setFormDataEnd", () => {
                    setNotToEmit();
                    this.formData = { ...this.formData, ...formData };
                });
                return;
            }

            setNotToEmit();
            if (delay) {
                setTimeout(() => {
                    this.formData = { ...this.formData, ...formData };
                },Number(delay))
            }else {
                this.formData = { ...this.formData, ...formData };
            }

        },
        validate() {
            let ref = this.$refs.form;
            return ref && this.$refs.form.validate(...arguments);
        },
        validateField() {
            let ref = this.$refs.form;
            return ref && this.$refs.form.validateField(...arguments);
        },
        resetFields() {
            this.$refs.form.resetFields();
            this.formData = this.getInitFormData();
        },
        unWatchMap() {
            [this.itemDependonWatchMap, this.itemChangeToEmitWatchMap].forEach(
                map => {
                    Object.keys(map).forEach(key => {
                        if (map[key] && typeof map[key] === "function") {
                            map[key]();
                        }
                    });
                }
            );
        },
        // 处理表单数据
        getHandleFormData() {
            let formData = { ...this.formData };
            (this.configData || []).forEach(item => {
                const key = item.key;

                if (typeof formData[key] === "string") {
                    formData[key] = formData[key] && formData[key].trim();
                }

                const defaultFormat = {
                    datetimerange: 'yyyy-MM-DD HH:mm:ss',
                    datetime: 'yyyy-MM-DD HH:mm:ss',
                    daterange: 'yyyy-MM-DD',
                    date: 'yyyy-MM-DD',
                    time: 'HH:mm'
                }
                let timeFormat = item.timeFormat || defaultFormat[item.type];
                try {
                    if (
                        formData[key] && formData[key].length &&
                        (item.type === "daterange" ||
                            item.type === "datetimerange" ||item.type === "time" || item.type === "render") &&
                        item.keyRange
                    ) {

                        formData[item.keyRange[0]] = formatDate(formData[
                            key
                        ][0], timeFormat);
                        formData[item.keyRange[1]] = formatDate(formData[
                            key
                        ][1], timeFormat);
                        // delete formData[key];
                    }
                    if (
                        formData[key] &&
                        (item.type === "date" || item.type === "datetime")
                    ) {
                        formData[key] =
                            typeof formData[key] !== "string"
                                ? formatDate(formData[key], timeFormat)
                                : formData[key];
                    }
                } catch (e) {
                    console.error(e);
                }
            });
            return formData;
        }
    },
    destroyed() {
        if (this.updateUnWatchHandle) {
            this.updateUnWatchHandle();
        }
        this.unWatchMap();
        // [this.itemDependonWatchMap, this.itemChangeToEmitWatchMap].forEach(
        //     map => {
        //         Object.keys(map).forEach(key => {
        //             if (map[key] && typeof map[key] === "function") {
        //                 map[key]();
        //             }
        //         });
        //     }
        // );
    }
};
